package dao

import (
	"context"
	"errors"
	"github.com/qiniu/qmgo"
	"github.com/samber/lo"
	"github.com/sirupsen/logrus"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"monoblog/config"
	"monoblog/domain/dto"
	"monoblog/domain/model"
	"monoblog/util"
	"strings"
	"sync"
)

var (
	articleRepo *ArticleRepository // 单例实例存储变量
	once1       sync.Once          // 确保初始化只执行一次的同步工具
)

// 初始化 QMGO 集合（单例模式）
func NewArticleRepository() *ArticleRepository {
	once1.Do(func() { // 并发安全的初始化逻辑，只会执行一次
		articleRepo = &ArticleRepository{
			collection:          config.GetDataBase().Collection(model.ArticleCollectionName), //指定集合名称
			blogStatsCollection: config.GetDataBase().Collection(model.BlogStatsCollectionName),
			ctx:                 context.Background(),
		}
	})
	return articleRepo // 返回唯一实例
}

type ArticleRepository struct {
	collection          *qmgo.Collection // QMGO 集合对象（替代原生 mongo.Collection）
	blogStatsCollection *qmgo.Collection
	ctx                 context.Context // 上下文（用于数据库操作超时控制）
}

func (impl *ArticleRepository) Add(article *model.Article) error {
	r, err := impl.collection.InsertOne(impl.ctx, article)

	if oid, ok := r.InsertedID.(primitive.ObjectID); ok {
		article.ID = oid.Hex()
	}
	return err
}

// UpdateByID - 按 ID 更新
func (impl *ArticleRepository) UpdateByID(id string, article model.Article) error {
	keyId, _ := primitive.ObjectIDFromHex(id)

	article.ID = "" // 清空ID，防止更新时被当作新数据处理
	update := util.ObjToUpdateMap(article)
	if len(update) == 0 {
		return nil
	}

	updateDoc := bson.M{"$set": update}

	err := impl.collection.UpdateId(impl.ctx, keyId, updateDoc)
	if errors.Is(err, mongo.ErrNoDocuments) {
		logrus.Warnf("record not found. dont update. id: %v", id)
		return nil
	}
	return err
}

// DeleteByID - 按 ID 删除
func (impl *ArticleRepository) DeleteByID(id string) error {
	keyId, _ := primitive.ObjectIDFromHex(id)

	// 执行删除操作
	err := impl.collection.RemoveId(impl.ctx, keyId)
	if errors.Is(err, mongo.ErrNoDocuments) {
		logrus.Warnf("article not found, Cannot be deleted. id: %v", id)
		return nil
	}
	return err
}

// FindByID 根据ID查询文章（包含文章内容字段、BlogStats数据）
func (impl *ArticleRepository) FindByID(id string) (*model.Article, error) {
	keyId, _ := primitive.ObjectIDFromHex(id)

	var article model.Article
	err := impl.collection.Find(impl.ctx, qmgo.M{"_id": keyId}).One(&article)

	if errors.Is(err, mongo.ErrNoDocuments) {
		logrus.Warnf("record not found. id: %v", id)
		return nil, errors.New("找不到数据, id: " + id)
	}

	//查询BlogStats数据
	if article.Status == model.PUBLISHED.Value() {
		if point, err2 := impl.FindBlogStats(id); err2 == nil {
			article.BlogStats = point
		}
	}

	return &article, err
}

// FindLiteByID 根据ID查询文章简单数据（不包含文章内容字段）
func (impl *ArticleRepository) FindLiteByID(id string) (*model.Article, error) {
	keyId, _ := primitive.ObjectIDFromHex(id)

	var article model.Article
	err := impl.collection.Find(impl.ctx, qmgo.M{"_id": keyId}).Select(bson.M{"content": 0}).One(&article)

	if errors.Is(err, mongo.ErrNoDocuments) {
		logrus.Warnf("record not found. id: %v", id)
		return nil, errors.New("找不到数据, id: " + id)
	}

	return &article, err
}

// FindCountByCategoryID 查询分类下的文章数量
func (impl *ArticleRepository) FindCountByCategoryID(cateId string, onlyPublished bool) (int, error) {
	filter := bson.M{"categoryId": cateId}
	if onlyPublished {
		filter["status"] = model.PUBLISHED.Value()
	}

	count, err := impl.collection.Find(impl.ctx, filter).Count()
	return int(count), err
}

// FindCountBySectionID 查询专栏下的文章数量
func (impl *ArticleRepository) FindCountBySectionID(sectionId string, onlyPublished bool) (int, error) {
	filter := bson.M{"sectionId": sectionId}
	if onlyPublished {
		filter["status"] = model.PUBLISHED.Value()
	}

	count, err := impl.collection.Find(impl.ctx, filter).Count()
	return int(count), err
}

// FindList - 按条件查询列表（不包含文章内容字段）
func (impl *ArticleRepository) FindList(query dto.ArticleQuery) ([]*model.Article, int, error) {
	var articles = make([]*model.Article, 0)

	filter := bson.M{} // 构建查询条件：空条件

	if query.AuthorId != "" {
		filter["authorId"] = query.AuthorId
	}
	if query.Title != "" {
		// 模糊查询标题，不区分大小写
		filter["title"] = bson.M{"$regex": query.Title, "$options": "i"}
	}
	if query.CategoryId != "" {
		filter["categoryId"] = query.CategoryId
	}
	if query.SectionId != "" {
		filter["sectionId"] = query.SectionId
	}
	if query.Status != 0 { //status 0 表示查询所有状态
		filter["status"] = query.Status
	}

	if len(query.Ids) > 0 {
		//id需要转换
		ids := lo.Map(query.Ids, func(item string, _ int) primitive.ObjectID {
			id, _ := primitive.ObjectIDFromHex(item)
			return id
		})
		filter["_id"] = bson.M{"$in": ids}
	}

	var total int
	if !query.IsZero() {
		//查询条数
		n, _ := impl.collection.Find(impl.ctx, filter).Count()
		total = int(n)
	}

	//分页查询，
	err := impl.collection.Find(impl.ctx, filter).
		Select(bson.M{"content": 0}).
		Sort("-createTime").
		Skip(int64(query.Offset())).Limit(int64(query.Limit())).
		All(&articles)
	return articles, total, err
}

// ExistsByTitle 根据名称+AuthorId, 查询文章是否存在
func (impl *ArticleRepository) ExistsByTitle(ignoreID string, title string, authorID string) (bool, error) {
	if title == "" {
		return false, nil
	}
	// 构建查询条件：
	filter := bson.M{
		"title":    title,
		"authorId": authorID,
	}
	if ignoreID != "" {
		keyId, _ := primitive.ObjectIDFromHex(ignoreID)
		filter["_id"] = bson.M{"$ne": keyId}
	}

	n, err := impl.collection.Find(impl.ctx, filter).Count()
	return n > 0, err
}

// GetStatusByID 根据ID,查询文章状态
func (impl *ArticleRepository) GetStatusByID(id string) (int, error) {
	keyId, _ := primitive.ObjectIDFromHex(id)

	// 定义临时结构体接收查询结果（仅包含status字段）
	var data struct {
		Status int `bson:"status"`
	}

	err := impl.collection.Find(impl.ctx, qmgo.M{"_id": keyId}).
		Select(bson.M{"status": 1}).One(&data)
	if errors.Is(err, mongo.ErrNoDocuments) {
		logrus.Warnf("record not found. id: %v", id)
		return 0, errors.New("找不到数据, id: " + id)
	}
	return data.Status, err
}

func (impl *ArticleRepository) FindBlogStats(id string) (*model.BlogStats, error) {
	//keyId, _ := primitive.ObjectIDFromHex(id)
	var point model.BlogStats

	err := impl.blogStatsCollection.Find(impl.ctx, qmgo.M{"_id": id}).One(&point)
	if errors.Is(err, mongo.ErrNoDocuments) {
		logrus.Warnf("record not found. id: %v", id)
		return nil, errors.New("找不到数据, id: " + id)
	}
	return &point, err
}

func (impl *ArticleRepository) FindBlogStatsByIds(ids []string) ([]*model.BlogStats, error) {
	//keyId, _ := primitive.ObjectIDFromHex(id)
	var points = make([]*model.BlogStats, 0)

	if len(ids) == 0 {
		return points, nil
	}

	err := impl.blogStatsCollection.Find(impl.ctx, qmgo.M{"_id": qmgo.M{"$in": ids}}).All(&points)
	if errors.Is(err, mongo.ErrNoDocuments) {
		logrus.Warnf("record not found. ids: %v", ids)
		return nil, errors.New("找不到数据, ids: " + strings.Join(ids, ","))
	}
	return points, err
}

func (impl *ArticleRepository) AddBlogStats(id string) error {
	point := model.BlogStats{ID: id}
	_, err := impl.blogStatsCollection.InsertOne(impl.ctx, &point)
	return err
}

func (impl *ArticleRepository) DeleteBlogStatsByID(id string) error {
	//keyId, _ := primitive.ObjectIDFromHex(id)

	// 执行删除操作
	err := impl.blogStatsCollection.RemoveId(impl.ctx, id)
	if errors.Is(err, mongo.ErrNoDocuments) {
		logrus.Warnf("record not found. dont delete. id: %v", id)
		return nil
	}
	return err
}

// UpdateBlogClickCount 更新文章阅读数 递增+1
func (impl *ArticleRepository) UpdateBlogClickCount(id string, delta int) error {
	//keyId, _ := primitive.ObjectIDFromHex(id)

	// 使用$inc操作符实现字段自增1
	updateDoc := bson.M{"$inc": bson.M{"clickCount": delta}}
	err := impl.blogStatsCollection.UpdateId(impl.ctx, id, updateDoc)
	return err
}

// UpdateBlogLikeCount 更新文章点赞数 递增±1
func (impl *ArticleRepository) UpdateBlogLikeCount(id string, delta int) error {
	//keyId, _ := primitive.ObjectIDFromHex(id)

	// 使用$inc操作符实现字段自增1
	updateDoc := bson.M{"$inc": bson.M{"likeCount": delta}}
	err := impl.blogStatsCollection.UpdateId(impl.ctx, id, updateDoc)
	return err
}

// UpdateBlogCommentCount 更新文章评论数 递增±1
func (impl *ArticleRepository) UpdateBlogCommentCount(id string, delta int) error {
	//keyId, _ := primitive.ObjectIDFromHex(id)

	// 使用$inc操作符实现字段自增1
	updateDoc := bson.M{"$inc": bson.M{"commentCount": delta}}
	err := impl.blogStatsCollection.UpdateId(impl.ctx, id, updateDoc)
	return err
}

// UpdateBlogFavoriteCount 更新文章收藏数 递增±1
func (impl *ArticleRepository) UpdateBlogFavoriteCount(id string, delta int) error {
	//keyId, _ := primitive.ObjectIDFromHex(id)

	// 使用$inc操作符实现字段自增1
	updateDoc := bson.M{"$inc": bson.M{"favoriteCount": delta}}
	err := impl.blogStatsCollection.UpdateId(impl.ctx, id, updateDoc)
	return err
}

// CountByUserId 查询用户的文章数量（按状态分组统计）
func (impl *ArticleRepository) CountByUserId(userId string) (map[string]int, error) {
	type StatusCount struct {
		Status int `bson:"_id"`   // 分组字段（status值）
		Count  int `bson:"count"` // 该状态的文章数量
	}

	pipeline := []bson.M{
		{"$match": bson.M{"authorId": userId}}, // 匹配用户文章
		{"$group": bson.M{
			"_id":   "$status",         // 按状态分组
			"count": bson.M{"$sum": 1}, // 统计数量
		}},
	}

	result := impl.collection.Aggregate(impl.ctx, pipeline)
	defer result.Cursor().Close()

	var counts []StatusCount

	if err := result.All(&counts); err != nil {
		return nil, err
	}

	totalCount := 0
	statusCounts := make(map[string]int)
	for _, item := range counts {
		if item.Status > 0 {
			status := model.ArticleStatus(item.Status)
			statusCounts[status.String()] = item.Count
			totalCount += item.Count
		}
	}
	statusCounts["total"] = totalCount
	return statusCounts, nil
}
